package com.econ.powercloud.ems.service.biz;

import com.econ.powercloud.clickhouse.dao.AnalogDataDAO;
import com.econ.powercloud.clickhouse.entity.AnalogData;
import com.econ.powercloud.ems.common.enums.CalLocationType;
import com.econ.powercloud.ems.common.util.KeepDecimalPointUtil;
import com.econ.powercloud.ems.dal.entity.EmsDeviceCalConfig;
import com.econ.powercloud.ems.pojo.ChartDataVO;
import com.econ.powercloud.ems.pojo.ForecastThreeChartVO;
import com.econ.powercloud.ems.pojo.ForecastChartVO;
import com.econ.powercloud.ems.pojo.ForecastTwoChartVO;
import com.econ.powercloud.ems.service.dao.*;
import com.econ.powercloud.ems.util.TimeUtil;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jwc
 * @since 2022/11/21
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class EnergyStationForecastService {
    private static final DecimalFormat DF = new DecimalFormat("#.##");
    private final EmsDeviceCalConfigDAO emsDeviceCalConfigDAO;
    private final AnalogDataDAO analogDataDAO;

    /**
     * 获取某个计算点号的折线图的数据
     * @return
     */
    public ForecastThreeChartVO getThreeChartData(CalLocationType calLocationType) {
        ForecastThreeChartVO vo = ForecastThreeChartVO.builder().yesterdayData(Lists.newArrayList())
                .todayData(Lists.newArrayList()).forecastData(Lists.newArrayList()).build();
        EmsDeviceCalConfig emsDeviceCalConfig = emsDeviceCalConfigDAO.selectOneByLocation(calLocationType.getCode());
        if (emsDeviceCalConfig == null) {
            log.warn("{}, 查询失败, ems_device_cal_config表没有查找到相应配置, location: {}", calLocationType.getDesc(), calLocationType.getCode());
            return vo;
        }
        Date nowDate = new Date();
        Long startTime = TimeUtil.getStartOfDay(nowDate);
        Long endTime = TimeUtil.getEndOfDay(nowDate);
        //获取当日的全站有功功率数据
        List<AnalogData> todayAnalogDatas = analogDataDAO.selectByPidAndTime(emsDeviceCalConfig.getComputeCode(), true, startTime, endTime);
        todayAnalogDatas.stream().forEach(a -> vo.getTodayData().add(ChartDataVO.builder().val(a.getV()).time(a.getT()).build()));
        //获取前7天的全站有功功率数据
        List<List<AnalogData>> list = Lists.newArrayList();
        List<List<ChartDataVO>> convertList = Lists.newArrayList();
        for (int i = 1; i < 8 ; i++) {
            Date lastDate = new Date(System.currentTimeMillis() - (i * TimeUtil.ONE_DAY_MILLIS));
            Long lastStartTime = TimeUtil.getStartOfDay(lastDate);
            Long lastEndTime = TimeUtil.getEndOfDay(lastDate);
            List<AnalogData> lastAnalogDatas = analogDataDAO.selectByPidAndTime(emsDeviceCalConfig.getComputeCode(), true, lastStartTime, lastEndTime);
            list.add(lastAnalogDatas);
            convertList.add(convert2FiveMinData(lastStartTime, lastEndTime, lastAnalogDatas));
        }
        //昨日的全站有功功率数据
        list.get(0).stream().forEach(a -> vo.getYesterdayData().add(ChartDataVO.builder().val(a.getV()).time(a.getT()).build()));
        //明日预测全站有功功率数据
        List<Long> times = Lists.newArrayList();
        Long tomorrowStartTime = startTime + TimeUtil.ONE_DAY_MILLIS;
        Long tomorrowEndTime = endTime + TimeUtil.ONE_DAY_MILLIS;
        for (long time = tomorrowStartTime; time<=tomorrowEndTime; time+= 5*60*1000){
            times.add(time);
        }
        for(int i = 0; i < times.size(); i++) {
            double totalVal = 0.0;
            int totalSize = 0;
            for (List<ChartDataVO> e : convertList) {
                if (!CollectionUtils.isEmpty(e) && StringUtils.isNotBlank(e.get(i).getVal())) {
                    totalVal += Double.valueOf(e.get(i).getVal());
                    totalSize ++;
                }
            }
            if (totalSize != 0) {
                double average = totalVal / totalSize;
                vo.getForecastData().add(ChartDataVO.builder().time(times.get(i)).val(DF.format(average)).build());
            }
        }
        return vo;
    }

    /**
     * 获取两个点号的图的数据
     * @param calLocationType 枚举
     * @return
     */
    public ForecastTwoChartVO getChartData(CalLocationType calLocationType) {
        ForecastTwoChartVO vo = ForecastTwoChartVO.builder().horizontalData(Lists.newArrayList()).data(Lists.newArrayList()).build();
        List<ForecastChartVO> list = Lists.newArrayList();
        List<EmsDeviceCalConfig> emsDeviceCalConfigs = emsDeviceCalConfigDAO.selectByLocation(calLocationType.getCode());
        if (CollectionUtils.isEmpty(emsDeviceCalConfigs)) {
            log.warn("{}, 查询失败, ems_device_cal_config表没有查找到相应配置, location: {}", calLocationType.getDesc(), calLocationType.getCode());
            return vo;
        }
        //横坐标时间轴的数据
        List<String> horizontalData = makeHorizontalData();
        //第一个点号
        EmsDeviceCalConfig chargeConfig = emsDeviceCalConfigs.get(0);
        list.add(ForecastChartVO.builder().name(chargeConfig.getName()).data(makeData(chargeConfig.getComputeCode())).build());
        //第二个点号
        //避免数组越界报错
        if(emsDeviceCalConfigs.size() > 1) {
            EmsDeviceCalConfig dischargeConfig = emsDeviceCalConfigs.get(1);
            List<String> result = makeData(dischargeConfig.getComputeCode());
            list.add(ForecastChartVO.builder().name(dischargeConfig.getName())
                    .data(result.stream().map(d -> KeepDecimalPointUtil.keepTwoPoint(Double.parseDouble(d) * 100)).collect(Collectors.toList()))
                    .build());
        }
        vo.setHorizontalData(horizontalData);
        vo.setData(list);
        return vo;
    }

    /**
     * 生成横坐标数据
     * @return
     */
    private static List<String> makeHorizontalData(){
        List<String> result = Lists.newArrayList();
        for (int i = 7; i > 0 ; i--) {
            long time = System.currentTimeMillis() - (i * TimeUtil.ONE_DAY_MILLIS);
            result.add(TimeUtil.transForTimeStrByTime(time, "MM.dd"));
        }
        result.add("今日");
        result.add("明日预测");
        return result;
    }

    /**
     * 获取某个点号的数据
     * @param pid
     * @return
     */
    private List<String> makeData(String pid) {
        List<String> result = Lists.newArrayList();
        List<AnalogData> list = Lists.newArrayList();
        for (int i = 7; i >= 0 ; i--) {
            long time = System.currentTimeMillis() - (i * TimeUtil.ONE_DAY_MILLIS);
            list.add(analogDataDAO.selectLastOneByTimeAndPid(pid, time));
        }
        //充电量数据集合
        list.stream().forEach(e -> result.add(Objects.isNull(e) ? null : e.getV()));
        //明日预测数据
        double totalVal = 0.0;
        int totalSize = 0;
        for (int i = 0 ; i < 7 ; i++){
            String val = result.get(i);
            if (StringUtils.isNotBlank(val)) {
                totalVal += Double.valueOf(val);
                totalSize ++;
            }
        }
        if (totalSize != 0) {
            double average = totalVal/totalSize;
            result.add(DF.format(average));
        }
        return result;
    }

    /**
     * 将数据整理成每5分钟一个数据点
     * @param startOfDay
     * @param endOfDay
     * @param dayOfdatas
     */
    private List<ChartDataVO> convert2FiveMinData(Long startOfDay, Long endOfDay,List<AnalogData> dayOfdatas) {
        List<ChartDataVO> result = Lists.newArrayList();
        if (CollectionUtils.isEmpty(dayOfdatas)) {
            return result;
        }
        //每5分钟的时间点
        List<Long> times = Lists.newArrayList();
        for (long time = startOfDay; time<=endOfDay; time+= 5*60*1000){
            times.add(time);
        }
        times.stream().sorted().forEach(time -> {
            AnalogData analogData = dayOfdatas.stream().filter(d -> d.getT() <= time).max(Comparator.comparingLong(AnalogData::getT)).orElse(null);
            result.add(ChartDataVO.builder().time(time).val(analogData==null ? null : analogData.getV()).build());
        });
        return result;
    }
}
